Accessibility-focused Unit Testing in Jest
On this page
In this section, we’ll become familiar with Jest by reading the source code for existing unit tests for the Icon component.
Open the icon.test.js file from inside the components/__tests__ directory, and follow along!
Reading Existing Icon Tests
At the very top of the file is a special docblock comment that tells Jest that we want to use jsdom as our test environment instead of the default Node.js environment. This allows us to write tests that behave as though we’re interacting with the DOM in a headless browser:
/**
* @jest-environment jsdom
*/Next, we import React and some of the helpers from Testing Library:
We will also import the Icon component that is the subject of our unit tests.
import React from 'react'
import { render, getByRole, getByLabelText } from '@testing-library/react'
import '@testing-library/jest-dom'
import userEvent from '@testing-library/user-event'
import Icon from "../icon"Jest uses describe() to group related tests in a named block.
For a string name in the describe statement we will match the component we’re testing and use Icon. The second argument to describe() is an arrow function with our test code.
Referencing the test() section in Jest’s docs, we can find that we need a name and a function to run the test. An optional third argument is a timeout for specifying how long to wait before aborting the test. We’ll stick with Jest’s default of 5 seconds (or more accurately, 5000 milliseconds).
The first test in this example looks to see if the Icon has a label when it is rendered. The second test will look for a role of img.
Jest includes it() as an alias for test(). I prefer to use “it” in order to have tests read more like sentences and to match other testing conventions.
The code for the Icon tests is as follows:
describe('Icon', () =>{
it('labels the icon', () => {
const nameFixture = "dock"
const { getByLabelText } = render(<Icon name={nameFixture} />)
const iconText = getByLabelText(nameFixture)
expect(iconText).toBeInTheDocument()
})
it('has an image role', () => {
const {getByRole} = render(<Icon name="wifi" />)
const icon = getByRole('img')
expect(icon).toBeInTheDocument()
})
})The getByLabelText query from Testing Library looks for elements with a given label. In this test, if the rendered Icon has an aria-label of “dock” the test will pass. Similarly, getByRole is used to see if the Icon has a role of img.
Testing Library features several query options outlined in their docs.
Either of the tests would fail if the queries didn’t find any elements, but I prefer to explicitly include the expect.
More About Matchers
The expect() method in Jest is used to test for truthiness with some help from matchers.
You can compare numbers, objects, and more in a variety of ways. Check our their docs for the full list of matchers.
The jest-dom library builds on this concept and provides matchers with a focus on the DOM and accessibility. We use toBeInTheDocument() in the Icon test, but there are many more to choose from.
Video Transcript
So we have icon button dot test JS and icon test JS.
That can be nice when you've got two files open, like the icon component and then the test and they have different file names. It's just helpful to have those in your, like here in vs code, all of the open files kind of up here in the open editors section or in your tabs. So for example, if we open up the icon in one tab and then come over here with the icon test and open that to the side, now we have icon and icon test next to each other.
And so for this test, this one is already set up. And I, so for this test on the right for icon test because we're using both we're using just for our unit tests, and then later on, for some puppeteer stuff, we declare that this environment is uses JS Dom, which is a kind of headless, JavaScript, rendering library.
I'm trying to think of the right word it's, it's like replacing what we, what the browser would give us for actually rendering. So JS DOM reimplements a lot of browser functionality in a headless environment. So you might find some edge cases with JSDOM, where it doesn't quite work the way we expected. Like, there's some features in the Dom like ranges that JSDOM, didn't implement for a long time.
And so sometimes you might run into quirks trying to test things that don't work quite the same as browsers do in JSDOM but it is really powerful and really useful. And you might go a long, long ways before you ever hit one of those JSDOM edge cases. I don't think we'll encounter any here. Just wanted to mention it to you, cuz it has come up for me before.
So we're using tools in Jest. no pun intended. and yeah, lots of really great tooling. I am gonna open our exercise, just read me.
In the Jest library, we're using testing library, which is fantastic. Absolutely adore testing library. And because this is a react project, we have the react flavor of testing library We have some lists here. All of our APIs that we're using in our just tests are listed here in the readme. And so you can check those out on GitHub. We also have a resources directory for this entire workshop that I will mention right now before I forget. So in our main read me, we've got all the way down at the bottom.
There is a link to our workshop resources. And so this has a bunch of stuff for both manual and automated testing as well as all the other workshops from the series. And so you can check those out and find a lot of this stuff, as well as in our project, in the readme on GitHub, within exercise one, just, this is where you can find a lot of these tools.
In the browser case, you don't aren't, don't have this open up in vs code, right at the moment, you can go to GitHub and check those out. Cool. So lots of resources, lots of APIs testing library, as I was mentioning is super awesome. And it gives us ways to render things in this headless context. So not opening up a real browser instance, kind of testing in memory using JSDOM
so we need to render a component kind of similar to how a browser would and testing library has really made that powerful and awesome.
So coming back to vs code, we've got testing library just Dom, which is in our just setup. So this might be redundant, but a new tool that I've been using increasingly it had a, it had a new update recently, I should say this testing library user event.
And so not really using this in this test, cuz the icon is not interactive, but we're gonna go and write some tests for an icon button. And that one will use testing library user event. And it, it recently had some changes to its API. So we're gonna use the new versions of things with user event. So for this test test, we're pulling in our icon, which I will pull back up here on the left.
So our icon component we're pulling in from up a level in our directory. So this is the test is in that test directory. We go up a level, pull in the icon component and we can test things like it takes in a label. So talking about, you know, props or options taking in input and making sure it lands in a place that renders something that's accessible.
This name prop on our icon currently is being put into an aria label attribute. Whereas if it didn't have aria label, we could pass in a name prop, but then what got rendered at the end, you know, it might not have accessible output for assistive technology to know what is the value of this particular, this particular icon.
So that's a test that we can write to assert that that option, that it takes in lands in the right spot. In this case, an ARIA label attribute, it also uses that same prop to match with an icon class. And then it's got a role of image. So we can check that this has a role of image so that this aria label attribute actually means something.
Otherwise it's just a span with a random aria attribute on it might not have the effect that we would expect in a screen reader. So the two tests that we have prewritten here over on the right, are that it labels the icon. So it takes in a name fixture in this case, the string dock, like it's a campground with a dock on a lake.
We can render that component. So the icon takes in that name fixture. So giving it some custom content that isn't doesn't exist anywhere else in the site. And we're gonna get the label text. So creating an object variable of get by label text. So kind of plucking that off of the testing library render method.
And once we've got a reference to that option that we've pulled off of testing libraries, API, we can use it to get the icon text. So get by label text, go, go get it by this custom name, fixture and store it in a variable called icon. Then we can expect that that contact is in the document. So testing library, get by label, text, assessing library, get by label text.
Let's look at that API, cuz it's helpful to know what that does. So there's a few variants of it. This will search for a label that matches given the text match and then find the element associated with that label. So it looks for things like label elements. It's also looking for aria label. So this specific API I chose for a reason was because I'm looking for something that will affect what's called the accessible name of this icon.
So ARIA label if it was a form input, we could use the same API to make sure that there is a labeled element and whenever that returns, then we can do further assertions on it. You know, is it an input? Is it a, does it have a role of image on it? That sort of thing. So let's get by label text. I've split out the image role part into a separate test to make these more granular and more specific for their purpose.
So it has an image role, which we can also pull off of testing libraries, render function with get by role. And once we have that, we can query icon get by role. And we could also say expect expect icon to be in the document may or may not need the expect. So some of these get by roles if they fail because they can't find the right thing.
You've already got a failing test, even without the expect, but we can go the full distance and make sure that that exists in the document. So to run this test, I'm gonna come over here to my terminal with control back tick, I can open this. I'm gonna kill off storybook. We don't need that anymore. And I can type clear to clear out my terminal, clean it up, make it look pretty and clean again.
So to run these tests that are currently enabled, I can say yarn test in the terminal and we'll run this. So there are a number of files that it's matching on. Some of them are commented out, so we have one failing here. This is in our icon button. So we're gonna go look at that in a second. That's another instance of a similar component, but within a, an interactive button.
So we have extra things that we could test, but for this one, our icon, it is working. And one thing I could do if I wanted to isolate this so. I just have to remember what tool am I using? It's either F I T or it dot only sometimes you can run a single test by adding an F in front of it or dot only. And if I wanted to see this test fail, I could come and remove that role of image over here.
Let's see if just will run a single test. The way I expected looks like it's running everything. So to fail, at least we could see it fail. So that get by role is failing. And the message says unable to find an accessible element with role of image. There are no accessible roles, but there might be some inaccesible roles if you wish to access them and set the hidden option to true whatever that means.
But we know that this ARIA label, you know, one of the, one of the things that we talked through so far was that for that ARIA label to be meaningful and not just be on static text this interactive or this widget role of image it adds meaning to ARIA label. It actually makes it attached to something that has a purpose.
And so role of image takes this graphical icon from CSS and marks it up into something that is more like an image tag with an alt attribute. But we can use this on icons. If we had CSS background images that we were trying to describe more as images, we could use a similar pattern. And so this should pass now and our test isolation was not working.
One thing I am gonna do is do yarn test dash dash watch. So I don't have to keep coming down here to the terminal and that will just watch for tests and run them all. So our icon. It's passing now we replaced that. Get by roll, but we're having some issues with our icon button. So let's go see what that one's about on the left.
I could do.
🛠 Challenge: Fix the Failing IconButton Label Test
There is a failing test for the IconButton component test file at components/__tests__/icon-button.test.js.
The failure message tells us that Jest isn’t able to find a label with the text of “Send it!"
Update the component code to make this test pass.
It’s helpful to split the your editor screen so you can look at the component code and test code side by side
🛠 Solution: Fixing the Failing Label Test
Let’s start by taking a look at the test code.
// inside icon-button.test.js
describe('IconButton', () =>{
it('labels the button', () => {
const textFixture = "Send it!"
const { getByLabelText } = render(<IconButton name={textFixture} />)
const buttonText = getByLabelText(textFixture)
expect(buttonText).toBeInTheDocument()
})
})Similar to before, we’re using the getByLabelText query to find a label in the rendered <IconButton> component.
Let’s look at the the source code for the component:
// components/icon-button.js
const IconButton = ({name, onClick}) => {
return (
<div
className="btn-submit btn-lookingglass"
data-testid="btn-submit"
onClick={(event) => { onClick(event)}}
>
<span className="icon-lookingglass-white"></span>
</div>
)
}The first thing you might notice is that the component is using a div instead of a button element— but that won’t solve our failing test. We’ll come back to this in a minute.
Our test fails because it couldn’t find a label, and it ends up the name prop that we pass in isn’t being used.
Adding aria-label={name} will make our test pass.
const IconButton = ({name, onClick}) => {
return (
<div
aria-label={name}
...Video Transcript
So I clicked into this kind of left side, so I can open a file over there and I'll do command P type icon button. We'll pull up the icon button and components. And then over here on the right, I could say icon button test, and we have a file in our test directory that like other ex exercises, you can take a peek in the answers directory, but we're gonna play with it right here in our vs code in the components directory.
So we're pulling in a lot of the same stuff we're using JS Dom. We're pulling in testing library, react functions like render screen. We're not actually gonna use fire event, but we've got some things already in here. Like get by text, get by label, text, get by role maybe we'll pull that in. We've got options.
So there's different APIs that you can use to query these elements and then make assertions on them, depending on what precise scenario you're trying to guard against. Sometimes using the queries, like get by role or get by label text. You're using the query itself to sort of assert accessibility behavior.
Even beyond the, you know, I expect it to have a role I've already tried to query it by its role. And if that doesn't work, then that's another way that I can sort of, you know, build this expectation into our test.
So we have one test in here for the icon button, says it labels the button it's taking in a text fixture of send it of course I can't. I cannot say it that way. So we've got, send it as our text fixture, our label, and we're doing a similar thing as our icon with get by label, text, plucking that off of the render method from testing library, passing in a name prop, getting the button text.
So checking if there is an element with that label text that got rendered. So did that name prop go anywhere and it's failing right now because that name is not being used. So this test of labeling the button is that's what is failing it, can't find unable to find a label with the text of send. It makes me laugh.
All right. So let's start by making this test pass. So. It's only gonna be halfway there, right? Like I can make this pass with an aria label of name, but that's not going to be super useful right now because this is a static div element. So yes, I could technically slap an ARIA label on it, but is that really gonna help accessibility of it?
No, because a div element is not focusable, we can't tab onto it. And fire off this click event. So it's not quite there. So to really round out our test for this icon button, we're gonna add some more test coverage to it.
Write New Tests for the IconButton
The <IconButton> component is used for the search button on the CampSpots homepage. When you use your keyboard to tab around the page, you’ll find that the button gets skipped over.
We’re going to add some new tests that make sure that the button is reachable and operable by the keyboard.
To help us trigger different types of events for testing user interactions, at the top of the test file we import user-event from Testing Library.
import userEvent from '@testing-library/user-event'To set up userEvent, we need to call a setup function and store it in an arbitrary variable such as user.
const user = userEvent.setup()Event calls like .tab() or .keyboard('[Enter]') will then hinge off of our stored variable.
Describe Tests Before Writing Them
Before diving into coding full tests, it can help to write the describe statements that reflect the behavior expected from the component you’re testing.
For accessibility unit tests, consider parts of the component’s API that impact user interaction and rendering of content inside the component. It’s a bit subjective how to divide up your tests but keeping them small and granular can make them easier to read.
For the button components, I expect that:
- The button has a label for users of Assistive Technology.
- The button can be reached with the keyboard
- The button can be operated with the keyboard and Assistive Tech.
We already know our button label works, so let’s write the reachability test.
Writing the Keyboard Operability Test
Following the structure of the existing test, add a new it() block and give it a description of the test we’ll be writing and and empty arrow function:
it('can be reached and operated with the keyboard', () => {
})Since the component we’re testing is like a submit or “send” button, I’m going to use names and labels with variations on sending. Don’t be afraid to have some fun with your test data! (I like to work my pets’ names into every codebase I can. E.g. name=”Rainier McCheddarton”)
Render the Component
The first thing we’ll do inside is tell JestDOM to render the IconButton component along with some test data. I’ll use “Fling it” for the name.
We also need to supply an onClick handler function. I’ll use an anonymous arrow function that sets the value of a clicked variable to be true (I’ll initialize the clicked variable before the render call).
By writing the test logic this way, we’ll be able to test that the click handler fired the way we expect it to. We’d be able to write a test that checked for simulated mouse clicks as well as the keyboard hitting enter.
Here’s what it looks like so far:
it('can be operated with the keyboard and assistive tech', () => {
let clicked = false
render(<IconButton name="Fling it" onClick={()=> { clicked = true }} />)Write your test as if it will pass, knowing it will fail until you fix the markup.
Create a Reference to the Component
Now that our button will be rendered by jsdom, we need a reference to it in order to interact with it. There are numerous ways to query for an element in unit tests, such as getByTestId, getByRole, getByLabel, and many more. Which one you should choose will depend on the purpose of the test.
Looking at the markup for the IconButton component, we can find there is a data-testid attribute with a value of "btn-submit".
This is the attribute we will use to tell Testing Library what to look for by calling screen.getByRole:
const button = screen.getByRole('button')Testing Library’s screen is a like a shorthand for document.body and getByRole is just one of the options for querying it. Check their docs for more!
Focus the Button & Simulate the Enter Key
Now we can call button.focus() to send focus to the button. If the button can’t be focused, the test will fail.
Calling .focus() will be common in your accessibility-focused unit tests.
Once the button has focus, we will simulate a user hitting the enter key on the keyboard.
Simulating user input with the keyboard with user-event is asynchronous. This means we’ll need to add the async keyword to our test’s arrow function, then we can await the user.keyboard pressing the Enter key.
If everything works, the button will have its click handler fired so the clicked variable will be set to true.
Here’s what the test code looks like all together:
it('can be operated with the keyboard and assistive tech', async () => {
let clicked = false
render(<IconButton name="Fling it" onClick={()=> { clicked = true }} />)
const button = screen.getByRole('button')
button.focus()
await user.keyboard('[Enter]')
expect(clicked).toBe(true)
})Now when we run Jest, the test fails because it can’t find an element with the role of button.
Running yarn test --watch will continuously run the tests as you save files.
Fixing the Failed Operability Test
Here’s a reminder of what the IconButton component code currently looks like:
const IconButton = ({name, onClick}) => {
return (
<div
aria-label={name}
className="btn-submit btn-lookingglass"
data-testid="btn-submit"
onClick={(event) => { onClick(event)}}
>
<span className="icon-lookingglass-white"></span>
</div>
)
}It might seem that adding role="button" would make the test pass, but that’s not how the browser works.
If you add the button role to the div-based IconButton, the test will still fail. This time it found the role, but there is no click handler that will toggle the clicked variable in the test!
If you were going to build a custom button component based on a div element, you would have to supply the role of button, set a tabindex of 0, and add a click handler.
But why do all of that work when you can use the semantic <button> element and get that functionality for free?
Changing the IconButton markup to use a <button> instead of a div will make the test pass as we want it to.
Video Transcript
So within this icon button test file, let's add some new assertions here for it.
It can be reached with the keyboard and we're going to write a failing test and make a pass. So it can be reached with the keyboard. So for this, we could start by rendering it. We don't need to pluck anything off of it for this part particular test. We're just gonna call render. So I'm gonna say icon button, instantiate this component.
We'll give it a name. And I'm just gonna put it right here on this where we instantiate this as opposed to a separate variable, but separate variable would work too. Let's say fling it instead of send it. Of course I'm when I say send it, I'm really thinking about my dog going after the tennis ball.
So fling it is a natural next step. Send it. Okay, so we've got fling it for the name. That's the first option or prop that this is expecting that it's also expecting a click handler or click function. So we can use this component and kind of pass down. Say this icon button is getting used within another component and that component needs to know what the click handler is going to do.
So we have this kind of chain of events being passed down. So we, this icon button to really be useful, needs to take in some sort of an event prop. So I could say on click cuz that's, what's expecting. And within this I could write kind of an anonymous function. I could also I'll write it anonymously, but it could be defined in a variable as well.
So for this, let's make an, an anonymous, arrow function. And inside of here, I'm gonna, let's let's make a function that flips a variable from false to true. So we can check was the variable, you know, it's false. Before we click this thing. If the click event actually fires kind of drilled down into this component, it should change this variable.
So we can actually test like, did that variable get changed or not? If the, if the click event didn't fire, like if we tab onto this element and or we aren't able to reach it from the keyboard, the click event, won't fire, it might work from the mouse, but if we've actually tried to activate it with the keyboard, this test should fail, cuz it won't fire the click event.
So I'm gonna create a variable before our render and say const actually we need let, cuz we're gonna change it. We'll say clicked equals false. Within our anonymous callback function, I'll say clicked equals true. And so that should, you know, if that click event fires should change that variable. So let's get a reference to our element and we're doing this in test driven development fashion.
So we're gonna write it as if it will pass, but it's gonna fail until we go and fix it over on the left in our icon button component. So I'm gonna say const button equals and I can, as opposed to kind of plucking a method off of render, I can also use screen. So screen dot, get by roll kind of multiple ways to get at some of these testing library, API methods.
So I can get by roll button and that is going to fail because there's no role of button over here right now. I mean, I could make this pass with roll of button, but that's still a div element. That's not reachable. So, and it doesn't have tab. Yeah. Cuz it's not reachable because it doesn't have tab index on it.
It's not a button element, even though we're adding a roll of button which I actually saw this on a website recently, like there's super inaccessible stuff. It might have even been an anchor without an href but they put roll of button on stuff. For some reason, like you didn't test that with the keyboard.
Did you happens all the time? So the role we'll do part of this work, we also need to tab onto it or try to tab onto it with the keyboard and using events. Can we reach and operate this thing? We're gonna convert this to a button in a second, but we'll write this test as if it was a button. So with this, get by role.
If we have a button, I should be able to call button dot focus. So this is important for accessibility testing. In a lot of these modern tools, I have found that calling dot focus on something. Gets us right to that element with the keyboard. And it will fail if we try to run it on an element, that's not, focusable like most of the modern tools these days will be like, eh, that's not focusable, you can't do that.
Which is what we want. We want the tools to kind of guard against writing tests that don't mimic reality. you can't focus on a div. Whereas in the past, sometimes the tooling wouldn't complain at you because it was like simulating events and it wasn't quite working the way that browsers worked. And so like when I started doing accessibility automation years ago, we didn't have as robust tooling as we do now.
Now we can rely on stuff like element dot focus. And if it's not, focusable it'll scream at you. That's a good thing. That's what we want. So I could say button dot focus, and this is where I'm going to call a weight. I need async up here in our test if I wanna call async await so in our it statement before I give it this anonymous arrow function, I'm gonna say a sync and then I can call a weight down here.
When I say user dot keyboard, and I'm going to pass a string with the command enterthat is what the user event API is expecting. If I wanna fire a, an enterkey it could be space. It could be arrow, right? It could be escape. There's a separate API for something like type, like if I wanted to type letters in there.
but they now have this API for user dot keyboard. And so I can fire commands at it. Tab is another one. I think they actually have user.tab so we can use that as well. So with this, I expect clicked to be true. This is gonna fail in a few ways because a there's no button roll over there. So looking down here, when I hit save unable to find an accessible element with the role of button, let's do this trick where we add roll of button, even though it's not focusable.
And see if we can get past that step just to see what the test does. So now we got to the next thing, so sure. It's got a button roll, but it didn't actually fire from the, the keyboard. So we fired a keyboard event on it and we expected our variable of clicked to be changed from false to true. It didn't actually work because you can't use a keyboard event to fire a click on a div element.
It, that's not the way it works in the browser. If we were trying to make a custom button with a div element, we would need a few other things sort of as a bit of trivia here. So we would need tab index of zero. We would also need an On-key handler, but why do that? When we can get rid of role of button and tab index and change this to a button that's so much shorter I love it.
Yeah. You get focus ability for free. You get that button roll for free the click event fires, and look at that our test pass. How cool is that? So now we are able to call screen dot get by roll, grab a button, whether it's bolted on with ARIA role of button or it's an implicit role of a button element. That piece would technically work, but by adding this functionality of trying to focus on it, trying to fire a keyboard event, and then checking that this callback function actually called, we've got a pretty robust test for keyboard support.
So we took something that was not working from the keyboard at all. We asserted some accessibility characteristics such as it's label that label then will really work in concert with our button roll and our keyboard events. And so from a screen reader perspective, that all comes together to make something that will be reachable.
Operable understandable, perceivable at no, I guess perceivable is one that includes alt text. So we've got something that's much more accessible now. But interestingly, some of those characteristics of this icon button, we had to write functional tests for, or do manual testing to uncover like, yeah, that div element div elements got some click events on it, but we had to go and test that with, you know, manually, and then we can bake that in with our automation.
So that's pretty cool. We've got, let's see. So I actually ended up combining two tests I got them all in here. So you could do it this way, this I think initially I intended to separate these out, so maybe we could do that real quick, but it could be reached with the keyboard. We're also operating over with the keyboard.
So I could say it can be reached and operated with the keyboard and assistive tech cuz this role of button is important. That really does impact functionality in a screen reader so that when we try to interact with the keyboard and we focus on that element. that button role being interactive is important, cuz that changes how the screen reader behaves on windows.
It can change what mode the screen reader is in. So, you know, key commands behave in certain ways. So when we have roles like button or text box that can really affect how a screen reader functions. So kind of baking this into our tests can make something that's robust that, you know, we didn't, it does really help to fire up a screen reader and make sure that things work.
But once you've kind of done some of that testing, you can bake it into your test and just assert that it's going to work as you expect.
🛠 Challenge: Write a Separate Keyboard Reachability Test
The test we just wrote checks for both reachability and operability.
However, the reachability part of the test was just calling button.focus().
There is utility in having a a separate keyboard specific reachability test.
It might help catch issues related to screen reader modes, or cases where something with a tabindex of -1 was being tested.
Using the above tests as your guide, write a separate test to check that the IconButton is reachable by the keyboard.
Try using a different query than getByRole. Look at the component markup for ideas.
🛠 Solution: Writing a Keyboard Reachability Test
I’ll start by writing out a new it() with a description of “can be reached by the keyboard” along with the arrow function.
This time instead of using getByRole, I’ll use getByTestId and pass in a btn-submit string to match the component’s test-id attribute.
The User Event library offers a user.tab() shorthand to simulate pressing the tab key.
I’ll then use Jest’s expect() method and check the button with the toHaveFocus() matcher from jest-dom.
Here’s what my reachability test looks like:
it('can be reached with the keyboard', () => {
render(<IconButton name="Chuck it" />)
const button = screen.getByTestId('btn-submit')
user.tab()
expect(button).toHaveFocus()
})Saving the file and running Jest, I can see that my test passes as expected.
Video Transcript
So if I were gonna separate these out, I could say we could make another, let's make another test real quick, make these a bit more granular so it can be reach with the keyboard just for funsies.
Since I love tests let's write another one. Why not? And so for this one we could, we could render and do icon button. Let's give it a name of Chuck it and the spirit of dogs and things that are not related to campgrounds. So for this one, I could say const button equals screen dot get by test ID. This is another way that we could target our buttons and things.
So this does have a data dash test ID of button dash submit. You might have various reasons to get things by, you know, using different APIs. The get by role I'm using down here in the next test. We could use that here as well, if we really wanted to. But I'm separating that out just to show you different options.
So we're getting that bias test ID. So from here, I could say user.tab and use that sort of utility function in user event. And then I can expect button to have focus. And this matcher to have focus is from the Jest Dom library, which is fantastic. Absolutely love it. So we've got that one. And so we have these two different tests.
It can be operated with the keyboard and it can be reached with the keyboard. So, I mean, the reachability is kind of already covered here with button dot focus. Like this would fail if it wasn't reachable, but can't hurt to have another one, like maybe this user tab you know, that does kind of give us some specific coverage in that maybe calling button dot focus you know, could technically pass if the button had tab index of negative one on it, like you can send focus to something with JavaScript using tabindex of negative one.
So depending on the kind of underlying test API, this reach or operability test here with button dot focus, if this had tab index of negative one, for some reason, like maybe on accident, this test could technically pass. And, but then we would have the issue where if I hit tab, you know, through the browser, as a user, I might never be able to reach this button because it's been removed from the tab order, even though I can technically focus it with JavaScript.
So I guess there is some utility in having a separate unit test that makes sure that this button is in the tab order. So I hit user.tab. Which I love that we have this API now, cause we didn't always so I can hit that and really te test is this thing in the tab order. And so that guards against this tab index of negative one issue.
So I hit save,
it's passing. I love it. We have a much more robust icon button now, and we've got some test coverage that can not only prevent us from breaking it, but it can kind of teach your team members like how this component should work. And so, yeah, I just, I love this stuff. It's so fun.